package jmine.tec.utils.cnpj;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author piercio
 */
public final class CnpjUtils {

    private static final int[] MULTI = { 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 };

    private static final int POSICAO_DOIS = 2;

    private static final int POSICAO_CINCO = 5;

    private static final int POSICAO_OITO = 8;

    private static final int POSICAO_DOZE = 12;

    private static final int POSICAO_QUATORZE = 14;

    private static final int MOD = 11;

    /**
     * Classe utilitária.
     */
    private CnpjUtils() {
        // vazio
    }

    /**
     * Gera os digitos verificadores de um CNPJ válido.
     * 
     * @param cnpj o número do CNPJ sem mascará (por exemplo 11.222.333/0001 deve ser 112223330001).
     * @return os dois digitos verificadores.
     */
    public static String gerarDV(String cnpj) {
        if (cnpj == null) {
            return null;
        }
        if (cnpj.length() != POSICAO_DOZE) {
            throw new IllegalArgumentException("CNPJ deve possuir 12 números, mas possui " + cnpj.length());
        }

        String dvs = "";
        int dv1 = 0;
        for (int i = 0; i < POSICAO_DOZE; i++) {
            dv1 += MULTI[i + 1] * Integer.valueOf(cnpj.substring(i, i + 1));
        }
        dv1 %= MOD;
        dvs += (dv1 < 2) ? Integer.toString(0) : Integer.toString(MOD - dv1);

        String cnpj1 = cnpj + dvs;
        int dv2 = 0;
        for (int i = 0; i < POSICAO_DOZE + 1; i++) {
            dv2 += MULTI[i] * Integer.valueOf(cnpj1.substring(i, i + 1));
        }
        dv2 %= MOD;
        dvs += (dv2 < 2) ? Integer.toString(0) : Integer.toString(MOD - dv2);

        return dvs;
    }

    /**
     * Verifica se o CNPJ é válido, ou seja, possui os digitos verificadores corretos.
     * 
     * @param cnpj o número do CNPJ.
     * @return <code>true</code> se os digitos verificadores baterem, <code>false</code> caso contrário.
     */
    public static boolean verificaCNPJ(String cnpj) {
        if (cnpj == null || cnpj.equals("")) {
            throw new IllegalArgumentException("CNPJ inválido: " + cnpj);
        }
        String cnpjSemMascara = removeMascara(cnpj);
        String dvs = gerarDV(cnpjSemMascara.substring(0, POSICAO_DOZE));
        return dvs.endsWith(cnpjSemMascara.substring(POSICAO_DOZE + 1));
    }

    /**
     * Remove a mascara de um CNPJ.
     * 
     * @param cnpjComMascara o CNPJ com máscara (p.e.: 11.222.333/0001-44)
     * @return o CNPJ sem a máscara (p.e.: 11222333000144)
     */
    public static String removeMascara(String cnpjComMascara) {
        if (cnpjComMascara == null || cnpjComMascara.equals("")) {
            return null;
        }

        /* Remove da String qualquer caractere que seja diferente de 0, 1, 2, 3, 4, 5, 6, 7, 8 ou 9. */
        Pattern pattern = Pattern.compile("[0-9]");
        Matcher m = pattern.matcher(cnpjComMascara);
        StringBuilder buffer = new StringBuilder();

        while (m.find()) {
            buffer.append(m.group());
        }

        return buffer.toString();
    }

    /**
     * Aplica a máscara ao CNPJ.
     * 
     * @param cnpjSemMascara o CNPJ sem máscara.
     * @return o CNPJ com máscara.
     */
    public static String aplicaMascara(String cnpjSemMascara) {
        if (cnpjSemMascara == null) {
            return null;
        }
        if (cnpjSemMascara.length() != POSICAO_QUATORZE) {
            throw new IllegalArgumentException("CNPJ deve possuir 14 números, mas possui " + cnpjSemMascara.length());
        }
        StringBuilder sb = new StringBuilder();
        sb.append(cnpjSemMascara.substring(0, POSICAO_DOIS));
        sb.append(".");
        sb.append(cnpjSemMascara.substring(POSICAO_DOIS, POSICAO_CINCO));
        sb.append(".");
        sb.append(cnpjSemMascara.substring(POSICAO_CINCO, POSICAO_OITO));
        sb.append("/");
        sb.append(cnpjSemMascara.substring(POSICAO_OITO, POSICAO_DOZE));
        sb.append("-");
        sb.append(cnpjSemMascara.substring(POSICAO_DOZE, POSICAO_QUATORZE));
        return sb.toString();
    }
}
